
package com.ton.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisTemplateConfig {
   @Bean
   public RedisTemplate<Object,Object> redisTemplate (RedisConnectionFactory connectionFactory ) {
     RedisTemplate <Object,Object> template = new RedisTemplate <> ();
     template .setConnectionFactory (connectionFactory );
     // 默认 redis 序列化的方式 使用 java 序列化 (IO), 换为 jackson 框架 json 序列化
     Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer (Object.class);
     ObjectMapper mapper = new ObjectMapper ();
     mapper .setVisibility (PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
     mapper .activateDefaultTyping (LaissezFaireSubTypeValidator.instance ,
ObjectMapper .DefaultTyping .NON_FINAL , JsonTypeInfo.As.PROPERTY );
     serializer .setObjectMapper (mapper );
     // 使用 StringRedisSerializer 来序列化和反序列化 redis 的key 值
     template.setKeySerializer (new StringRedisSerializer());
     // 值需要使用 json 来序列化
     template.setValueSerializer (serializer);
     // Hash 的key 也采用 StringRedisSerializer 的序列化方式
     template.setHashKeySerializer (new StringRedisSerializer());
     // 值需要使用 json 来序列化
     template.setHashValueSerializer (serializer );
     template.afterPropertiesSet ();
     return template ;
  }
   /**
   * 使用 redis 执行 lua 脚本
   *
   * @return 返回限流当前的值
   */
   @Bean
   public DefaultRedisScript<Long> limitScript () {
     DefaultRedisScript <Long> redisScript = new DefaultRedisScript<>();
     redisScript.setScriptText (limitScriptText ());
     redisScript.setResultType (Long.class );
     return redisScript;
  }
   /**
   * 限流脚本
   */
   private String limitScriptText () {
     return "local key = KEYS[1]\n" +
         "local count = tonumber(ARGV[1])\n" +
         "local time = tonumber(ARGV[2])\n" +
         "local current = redis.call('get', key);\n" +
         "if current and tonumber(current) > count then\n" +
         "  return tonumber(current);\n" +
         "end\n" +
         "current = redis.call('incr', key)\n" +
         "if tonumber(current) == 1 then\n" +
         "  redis.call('expire', key, time)\n" +
         "end\n" +
         "return tonumber(current);" ;
  }
}
